﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BrowserGameFramework.Common;
using BrowserGameFramework.DBConnector;
using BrowserGameFramework.DBConnector.Interfaces;
using BrowserGameFramework.DBConnector.SqlServer;
using System.Data.SqlClient;
using System.Xml.XPath;
using System.Xml.Serialization;
using System.IO;
using System.Xml.Linq;
using BrowserGameFramework.Common.Configuration;
using BrowserGameFramework.Common.Logging;
using System.Xml;
using BrowserGameFramework.Common.Exceptions;
using BrowserGameFramework.Mail;

namespace BrowserGameFramework.Account
{
    /// <summary>
    /// Handels accounting operations.
    /// </summary>
    public class AccountHandler : BaseLoginHandler
    {
        /// <summary>
        /// Userlogin with credentials
        /// </summary>
        /// <param name="user">Username</param>
        /// <param name="pass">Cleartext Password</param>
        /// <returns>Userprincipal with filled roles.
        /// Return null, if the user is unknown</returns>
        public override IUserprincipal Login(string user, string pass)
        {
            string pw = base.GetMd5FromString(pass);

            SqlDbHandler db = SqlDbHandler.GetInstance();
            SqlCommand cmd = new SqlCommand("GetUser");

            cmd.Parameters.Add(user.BuildInputStringParam("UserLoginName"));
            cmd.Parameters.Add(pw.BuildInputStringParam("Pass"));
            XPathDocument xDoc = db.CallXmlProcedureSelect(cmd);

            IUserprincipal prince = BuildPrincipal(xDoc);

            return prince;
        }

        /// <summary>
        /// Sets a new User to the Database. Do duplicates check.
        /// </summary>
        /// <returns>Enum with resultstate</returns>
        public Enums.RegistrationStatus RegisterUser(string loginName, string userName, string password, string email)
        {
            try
            {
                Trace.TraceEnterEvent("RegisterUser");

                bool registerEnabled = AppConfMan.GetKeyAsBool("RegistrationEnabled");
                int maximumUser = AppConfMan.GetKeyAsInt("MaximumActiveUserInGame");
                Enums.RegistrationStatus resultState = new Enums.RegistrationStatus();

                // TODO: Implement MaximumUser
                int currentUser = 0;

                if (maximumUser != 0)
                {
                    SqlDbHandler handler = SqlDbHandler.GetInstance();
                    SqlCommand cmd = new SqlCommand();
                    cmd.CommandText = "GetNumberOfUser";
                    XmlDocument doc = handler.CallProcedureExecute(cmd);
                    Trace.TraceMessage("Response doc " + doc.OuterXml, Enums.TraceLevel.Verbose);

                    if (doc != null)
                    {
                        string xpath = "";
                        XmlNode node = doc.DocumentElement.SelectSingleNode(xpath);

                        if (node == null)
                        {
                            throw new BGFBackendException(string.Format("The XmlNode was empty. Xpath wrong? Xpath '{0}' Target Xml'{1}'", xpath, doc.OuterXml));
                        }

                        if (string.IsNullOrEmpty(node.Value))
                        {
                            throw new BGFBackendException("The XmlNode has no value. Check the Procedure GetNumberOfUser.");
                        }

                        currentUser = Convert.ToInt32(node.Value);
                    }
                    else
                    {
                        Trace.TraceError("XmlDocument for GetNumberOfUser was empty");
                    }
                }

                if (registerEnabled && (maximumUser == 0 || currentUser != maximumUser))
                {
                    // TODO Duplicate Check
                    CreateUser(loginName, userName, password, email);

                    bool emailVerification = AppConfMan.GetKeyAsBool("EmailVerification");

                    if (emailVerification)
                    {
                        string activationKey = GetActivationKey(loginName);
                        string link = AppConfMan.GetKeyAsString("RegistrationActivationUrl");
                        Uri activationLink = new Uri(link + activationKey);

                        MailHandler.SendRegistrationMail(email, activationLink);
                        resultState = Enums.RegistrationStatus.SuccessButWaitingForUnlock;
                    }
                    else
                    {
                        resultState = Enums.RegistrationStatus.SuccessAndReady;
                    }
                }
                else
                {
                    resultState = Enums.RegistrationStatus.RegistrationIsDisabled;
                }

                Trace.TraceLeaveEvent("RegisterUser");

                return resultState;
            }
            catch (Exception ex)
            {
                Trace.TraceExceptionError("Error on registration", ex);
                throw ex;
            }
        }

        /// <summary>
        /// Sets a new user to database
        /// </summary>
        /// <param name="loginName">User Loginname</param>
        /// <param name="userName">User Ingame Name</param>
        /// <param name="password">User Password</param>
        /// <param name="email">User Email</param>
        private void CreateUser(string loginName, string userName, string password, string email)
        {
            Trace.TraceEnterEvent("CreateUser");

            SqlCommand cmd = new SqlCommand();
            cmd.Parameters.Add(loginName.BuildInputStringParam("LoginName"));
            cmd.Parameters.Add(userName.BuildInputStringParam("UserName"));
            cmd.Parameters.Add(password.BuildInputStringParam("Password"));
            cmd.Parameters.Add(email.BuildInputStringParam("Email"));
            cmd.CommandText = "CreateUser";

            SqlDbHandler handler = SqlDbHandler.GetInstance();
            handler.CallProcedureExecute(cmd);

            Trace.TraceLeaveEvent("CreateUser");
        }

        /// <summary>
        /// Builds the principal object with functions.
        /// </summary>
        /// <param name="xDoc">Xpath documnt with userinformation</param>
        /// <returns></returns>
        private IUserprincipal BuildPrincipal(XPathDocument xDoc)
        {
            Trace.TraceEnterEvent("BuildPrincipal");

            XPathNavigator navi = xDoc.CreateNavigator();

            if (string.IsNullOrEmpty(navi.OuterXml))
            {
                return null;
            }

            int userId = Convert.ToInt32(navi.SelectSingleNode("User/UserId").ValueAs(typeof(Int32)));
            string[] roles = GetFunctions(userId);

            UserPrincipal principal = new UserPrincipal(roles);
            principal.ID = userId;
            principal.Name = navi.SelectSingleNode("User/UserName").Value;
            principal.Email = navi.SelectSingleNode("User/Email").Value;

            Trace.TraceLeaveEvent("BuildPrincipal");

            return principal;
        }

        /// <summary>
        /// Gets all functions for a user.
        /// </summary>
        /// <param name="userId">User ID</param>
        /// <returns>Array of String with functions</returns>
        private string[] GetFunctions(int userId)
        {
            Trace.TraceEnterEvent("GetFunctions");

            XPathDocument functions;
            SqlDbHandler handler = SqlDbHandler.GetInstance();
            SqlCommand cmd = new SqlCommand("GetFunctions");
            cmd.Parameters.Add(userId.BuildInputInt32Param("userid"));

            List<string> result = new List<string>();

            functions = handler.CallXmlProcedureSelect(cmd);
            XPathNavigator navi = functions.CreateNavigator();
            XPathNodeIterator it = navi.Select("Functions/Functionname");

            while (it.MoveNext())
            {
                result.Add(it.Current.Value);
            }

            Trace.TraceLeaveEvent("GetFunctions");
            return result.ToArray();
        }

        /// <summary>
        /// Gets the activation key for the url
        /// </summary>
        /// <param name="loginName">Username</param>
        /// <returns>activation key</returns>
        private string GetActivationKey(string loginName)
        {
            return GetMd5FromString(loginName + DateTime.Now.ToString());
        }

        private void GetRoles()
        {
            throw new NotImplementedException();
        }

        private void GetGroups()
        {
            throw new NotImplementedException();
        }

        private void SetGroup()
        {
            throw new NotImplementedException();
        }
    }
}
